﻿using Microsoft.Build.Framework;
using Codeplex.STSDTS.MSBuildTasks.DeploymentServices;
using System.Net;
using Codeplex.STSDTS.Entities;
using System.Collections.Generic;
using System.IO;
using System;

namespace Codeplex.STSDTS.MSBuildTasks
{

    /// <summary>
    /// Class representing a build task for use with Team Foundation Build.
    /// This task invokes the STSDTS deployment web service and auto deploys the solutions as described in the farm XML file.
    /// </summary>
    public class InstallFarm : BaseTask
    {

        #region Properties

        /// <summary>
        /// Url of the deployment web service
        /// </summary>
        [Required]
        public string DeploymentServiceUrl
        {
            get;
            set;
        }

        /// <summary>
        /// Location of the farm XML file
        /// </summary>
        [Required]
        public string FarmFile
        {
            get;
            set;
        }

        /// <summary>
        /// Location where WSPs are stored on disk
        /// </summary>
        [Required]
        public string BinariesLocation
        {
            get;
            set;
        }

        /// <summary>
        /// Username for calling the deployment web service
        /// </summary>
        [Required]
        public string UserName
        {
            get;
            set;
        }

        /// <summary>
        /// Password for calling the deployment web service
        /// </summary>
        [Required]
        public string Password
        {
            get;
            set;
        }

        /// <summary>
        /// Domain for calling the deployment web service
        /// </summary>
        public string Domain
        {
            get;
            set;
        }

        private DeploymentService _deploymentService;

        #endregion Properties

        /// <summary>
        /// Install collection of solutions to farm that runs given web service
        /// </summary>
        /// <param name="solutions">Collection of solutions</param>
        private void InstallSolutions(IEnumerable<Solution> solutions)
        {
            //Loop through solutions
            foreach (var solution in solutions)
            {
                var taskId = CreateBuildStep("Installing {0}", solution.FileName);
                //Get file from disk and put it into a buffer
                var solutionFile = new FileInfo(string.Format("{0}{1}", BinariesLocation, solution.FileName));
                using (var reader = solutionFile.OpenRead())
                {
                    var buffer = new byte[reader.Length];
                    reader.Read(buffer, 0, buffer.Length);
                    //Invoke deployment web service
                    try
                    {
                        _deploymentService.InstallSolution(buffer, solution.Id, solution.FileName);
                        //Log invocation
                        EndBuildStep(taskId, true);
                        Log("InstallSolution({0}, {1}) succeeded", solution.Id, solution.FileName);
                    }
                    catch (Exception ex)
                    {
                        EndBuildStep(taskId, false);
                        Log("InstallSolution({0}, {1}) error: {2}",solution.Id, solution.FileName, ex.Message);
                    }
                }
            }
        }

        /// <summary>
        /// Deploys given solutions
        /// </summary>
        /// <param name="solutions">Solutions to deploy</param>
        private void DeploySolutions(IEnumerable<Solution> solutions)
        {
            //Loop through all solutions
            foreach (var solution in solutions)
            {
                var taskId = CreateBuildStep("Deploying {0}", solution.FileName);
                //Compose the web applications to deploy the solution to
                List<string> webApplicationUrls = null;
                if (solution.WebApplications != null && solution.WebApplications.Count > 0)
                {
                    webApplicationUrls = new List<string>();
                    foreach (var webApplication in solution.WebApplications)
                    {
                        webApplicationUrls.Add(webApplication.Url);
                    }
                }
                try
                {
                    //Invoke deployment web service
                    _deploymentService.DeploySolution(solution.Id,
                                                      webApplicationUrls != null ? webApplicationUrls.ToArray() : null);
                    //Log invocation
                    EndBuildStep(taskId, true);
                    Log("DeploySolution({0}) succeeded", solution.Id);
                }
                catch (Exception ex)
                {
                    EndBuildStep(taskId, false);
                    Log("DeploySolution({0}) error: {1}", solution.Id, ex.Message); 
                }
                
            }
        }

        /// <summary>
        /// Activates features in given solution
        /// </summary>
        /// <param name="solution">Solution to deploy</param>
        private void ActivateFeatures(Solution solution)
        {
            //Loop features in given solution
            foreach (var feature in solution.Features)
            {
                //Activate features on sites
                foreach (var site in feature.Sites)
                {
                    var taskId = CreateBuildStep("Activating {0} at {1}", feature.Id, site.Url);
                    try
                    {
                        _deploymentService.ActivateFeatureOnWeb(feature.Id, site.Url);
                    }
                    catch (Exception ex)
                    {
                        EndBuildStep(taskId, false);
                        Log("ActivateFeatureOnWeb({0}, {1}) error: {2}",feature.Id, site.Url, ex.Message);
                    }
                    Log("ActivateFeatureOnWeb({0}, {1}) succeeded", feature.Id, site.Url);
                    EndBuildStep(taskId, true);
                }
                //Activate features on site collections
                foreach (var siteCollection in feature.SiteCollections)
                {
                    var taskId = CreateBuildStep("Activating {0} at {1}", feature.Id, siteCollection.Url);
                    try
                    {
                        _deploymentService.ActivateFeatureOnSite(feature.Id, siteCollection.Url);
                        EndBuildStep(taskId, true);
                        Log("ActivateFeatureOnSite({0}, {1}) succeeded", feature.Id, siteCollection.Url);
                    }
                    catch (Exception ex)
                    {
                        EndBuildStep(taskId, false);
                        Log("ActivateFeatureOnSite({0}, {1}) error: {2}", feature.Id, siteCollection.Url, ex.Message);
                    }

                }
                //Activate feature on web applications
                foreach (var webApplication in feature.WebApplications)
                {
                    var taskId = CreateBuildStep("Activating {0} at {1}", feature.Id, webApplication.Url);
                    try
                    {
                        _deploymentService.ActivateFeatureOnWebApplication(feature.Id, webApplication.Url);
                        EndBuildStep(taskId, true);
                        Log("ActivateFeatureOnWebApplication({0}, {1}) succeeded", feature.Id, webApplication.Url);
                    }
                    catch (Exception ex)
                    {
                        EndBuildStep(taskId, false);
                        Log("ActivateFeatureOnWebApplication({0}, {1}) error: {2}", feature.Id, webApplication.Url, ex.Message);
                    }
                }
            }
        }

        override public bool Execute()
        {
            //Load farm file
            var farm = Farm.Deserialize(FarmFile);

            //Setup deployment service with given credentials
            _deploymentService = new DeploymentService
                                        {
                                            Credentials =
                                                new NetworkCredential
                                                {
                                                    UserName = UserName,
                                                    Password = Password
                                                },
                                            Url = DeploymentServiceUrl
                                        };
            //Also set domain if it is not null or empty
            if (!string.IsNullOrEmpty(Domain))
                ((NetworkCredential)_deploymentService.Credentials).Domain = Domain;

            //Install solutions
            InstallSolutions(farm.Solutions);

            //Deploy solutions
            DeploySolutions(farm.Solutions);

            //Activate features
            foreach (var solution in farm.Solutions)
            {
                ActivateFeatures(solution);
            }
            return true;
        }

    }
}
